#include "config.h"

#include <stdlib.h>
#include <stdio.h>

#include "libgphoto2/i18n.h"

#include "crc.h"


/*
 * CRC 0102010
 */
static unsigned short crc_table[256] = {
	0x0, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
	0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
	0x1081, 0x108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
	0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
	0x2102, 0x308b, 0x210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
	0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
	0x3183, 0x200a, 0x1291, 0x318, 0x77a7, 0x662e, 0x54b5, 0x453c,
	0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
	0x4204, 0x538d, 0x6116, 0x709f, 0x420, 0x15a9, 0x2732, 0x36bb,
	0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
	0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x528, 0x37b3, 0x263a,
	0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
	0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x630, 0x17b9,
	0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
	0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x738,
	0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
	0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
	0x840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
	0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
	0x18c1, 0x948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
	0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
	0x2942, 0x38cb, 0xa50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
	0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
	0x39c3, 0x284a, 0x1ad1, 0xb58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
	0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
	0x4a44, 0x5bcd, 0x6956, 0x78df, 0xc60, 0x1de9, 0x2f72, 0x3efb,
	0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
	0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0xd68, 0x3ff3, 0x2e7a,
	0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
	0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0xe70, 0x1ff9,
	0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
	0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0xf78,
};


/*
 * Initial CRC values. Determined by looking at CRCs sent by the camera.
 */

static int crc_init[1024] = {
	-1, -1, -1, -1, -1, 0x83a4, 0x5b70, 0x724e,
	0x0ee6, 0x5969, 0x7b6c, 0x647f, 0x8e90, 0x07ad, 0x5af0, 0xfa5f,
	0x77aa, 0xc2b3, 0x5003, 0x59f5, 0xe76c, 0xad67, 0xe2af, 0x4632,
	0xd983, 0xb9a8, 0x8cfb, 0x7c8f, 0xbf08, 0x1c9d, 0xb35b, 0x2f51,
	0xec49, 0xd0dc, 0xae31, 0xac9c, 0x11be, 0xf886, 0xbe88, 0x948c,
	0xca17, 0xb48b, 0xc726, 0xed56, 0xc7cd, 0x0656, 0xa9e1, 0x44eb,
	0x10a1, 0xef97, 0x16ef, 0x91f1, 0x9f42, 0x5cac, 0x9639, 0x6f35,
	0x9c2b, 0x2d9f, 0x326b, 0x3f84, 0xb859, 0x75ea, 0x9291, 0xe771,
	0xb067, 0x0b62, 0xf53c, 0x6c55, 0xe418, 0xc154, 0xaf30, 0xa58d,
	0x4827, 0xbc6d, 0x61ae, 0x77c5, 0xadb3, 0x36af, 0xdbc0, 0xea8a,
	0x23ba, 0x6785, 0x6ca3, 0x1218, 0x46b5, 0x5e83, 0xa91b, 0xbeeb,
	0xf78c, 0xcc77, 0xe4ed, 0x3454, 0x30e2, 0xa6a6, 0x7b14, 0x1c7f,
	0x515b, 0x09e4, 0x631e, 0xd7e7, 0xad46, 0xc3af, 0x4412, 0xe9a1,
	0x1089, 0xc797, 0x5c56, 0x6c39, 0x8818, 0xbfcb, 0xdf9d, 0x97ce,
	0x9024, 0x4253, 0x98c7, 0xe1db, 0x2a01, 0x941c, 0x5a17, 0x1d5f,
	0x794a, 0x525d, 0x17d7, 0xa1e0, 0x0563, 0x84d2, 0x1507, 0x61c2,
	0x1bc5, 0xd32c, 0x4602, 0xe983, 0x3289, 0xdd84, 0x9eec, 0xfabd,
	0x95aa, 0xe406, 0xdf54, 0x5ece, 0xe41b, 0xc254, 0xb703, 0x5715,
	0x7782, 0xeab3, 0x1aba, 0xa43d, 0xf036, 0x4e00, 0xab0b, 0xbec9,
	0xd58c, 0xd664, 0x2657, 0xa2d0, 0x2d50, 0xfd6b, 0x7bdd, 0xd57f,
	0x2564, 0x89e3, 0x4cda, 0x6129, 0xf0c5, 0xbd00, 0x04bf, 0x50c3,
	0x99f5, 0xdbca, 0xe08a, 0x7310, 0x58f7, 0xed7d, 0xeccd, 0x54dc,
	0xa6b1, 0x6c14, 0xa518, 0xdd27, 0x3dec, 0xc07b, 0x8821, 0x86cb,
	0x1c25, 0x0b5b, 0xcc3c, 0xafed, 0x788d, 0x9d4c, 0x428e, 0x45c7,
	0x34b0, 0xd4e2, 0xb075, 0x1962, 0x640e, 0xff90, 0x90ff, 0x9953,
	0x7dca, 0xf219, 0x7122, 0x7ad5, 0xd56e, 0x3464, 0x00e2, 0x2d87,
	0x2a6b, 0xfe1c, 0x14ee, 0x80d3, 0x3443, 0x27e2, 0x1fc1, 0xf768,
	0x2877, 0xf23e, 0x5622, 0x4893, 0x086d, 0xe20f, 0xe632, 0xfb76,
	0x56bb, 0xd193, 0xe920, 0x9189, 0xe742, 0x8367, 0x9870, 0x56db,
	0xb193, 0xf773, 0x3377, 0x2b95, 0x080d, 0x820f, 0xf861, 0x5988,
	0x9a6c, 0x5af9, 0xf35f, 0x3f33, 0x0f59, 0xee78, 0xf1fe, 0x8e11,
	0x86ad, 0x7a25, 0x256e, 0x83e3, 0x1c70, 0x5e5b, 0x711b, 0x43d5,
	0x16d6, 0xa8f1, 0x5cfa, 0xc039, 0xca21, 0x828b, 0x7c61, 0x5108,
	0x5ae4, 0xee5f, 0xd6fe, 0xbc57, 0x5bae, 0xac4e, 0xc3be, 0x5512,
	0x60a0, 0x71d4, 0x8cd5, 0x528f, 0xc5d7, 0x0c74, 0xdb4b, 0x618a,
	0x53c5, 0x87c6, 0x1934, 0x320e, 0x5a84, 0x8e5f, 0xc8ad, 0x1ea9,
	0x9779, 0x2724, 0xd9c1, 0xfba8, 0x88bb, 0x1ccb, 0xe55b, 0x8a45,
	0xf2e9, 0x8122, 0xcd52, 0xc9fc, 0x47b8, 0x5b92, 0x904e, 0x2853,
	0xd63e, 0x7c57, 0x6708, 0xe1a3, 0x5201, 0x4bd7, 0x545e, 0x24b1,
	0x54f2, 0x88b1, 0x16cb, 0xb5f1, 0xb537, 0x7337, 0x7ff7, 0xdf3b,
	0x31ce, 0x82b7, 0x4061, 0xbae5, 0xd9c8, 0xf2a8, 0xc022, 0xd121,
	0x5b20, 0x224e, 0x9b94, 0xaae8, 0x55d8, 0xaaa0, 0x1dd8, 0xfe4a,
	0x42ee, 0x25c7, 0x2ae3, 0x761c, 0x7ca2, 0x9208, 0x7e71, 0x512a,
	0x78e4, 0xf44c, 0x1444, 0x2ad3, 0x461c, 0xf783, 0xc377, 0x9c12,
	0x149f, 0xf1d3, 0xa311, 0xe441, 0x9854, 0x72db, 0x9be6, 0xd8e8,
	0xdab9, 0x9b9b, 0xa5e8, 0x2d27, 0x8a6b, 0xdce9, 0xfbfd, 0xddbb,
	0xa1ec, 0x0963, 0xe41e, 0xc754, 0x9f56, 0x48ac, 0x376d, 0x11d1,
	0x9786, 0xd824, 0x16b9, 0xc7f1, 0x3a56, 0x420c, 0xc7c7, 0x0c56,
	0xf94b, 0x7b99, 0x917f, 0x1142, 0x0486, 0x69c3, 0x5a4d, 0x475f,
	0xbc92, 0x9eae, 0xb8bd, 0x91ea, 0x8442, 0x8507, 0xc816, 0xa5a9,
	0x6c27, 0x9618, 0x4e35, 0x9e0b, 0x1dbd, 0x9b4a, 0x74e8, 0x9880,
	0xa6db, 0x0614, 0xebe1, 0x40ab, 0x70e5, 0xb5c4, 0x8037, 0xd043,
	0x3131, 0x7db7, 0x8f19, 0x86bc, 0x6b25, 0xac6f, 0xe2be, 0x5732,
	0x5082, 0xd8f5, 0xc7b9, 0x7256, 0x16e6, 0x98f1, 0xd7db, 0x9146,
	0x2842, 0xc73e, 0xf556, 0x0655, 0xaae1, 0x5cd8, 0xe239, 0xd032,
	0x4031, 0xeae5, 0x4cba, 0x0129, 0xee96, 0x1ffe, 0xc868, 0xdba9,
	0x838a, 0x7570, 0x0891, 0x1e0f, 0x3179, 0x35b7, 0xdbf3, 0xd98a,
	0xb0a8, 0xc462, 0xb165, 0x0173, 0xb496, 0xda26, 0x049b, 0x74c3,
	0xb380, 0xf451, 0x0944, 0xc31e, 0xf512, 0x4255, 0x9ec7, 0xd1bd,
	0xc720, 0xeb56, 0xf7ab, 0xeb77, 0xd6ab, 0xe957, 0xe689, 0x4076,
	0xade5, 0x60af, 0x7ed4, 0xf42a, 0x7244, 0x04e6, 0x09c3, 0x441e,
	0xe5a1, 0x7045, 0x15c4, 0xa2c2, 0x3f50, 0x6c59, 0xe818, 0xa198,
	0x7d63, 0x5b19, 0x1b4e, 0x582c, 0x367d, 0x09c0, 0x471e, 0xfd92,
	0x82dd, 0x2a61, 0xf41c, 0x4444, 0xbfa1, 0xb59d, 0xd937, 0x0da8,
	0x0f5a, 0xed78, 0xe9cd, 0x7c89, 0xb908, 0x2cfb, 0x5e7a, 0x501b,
	0x41f5, 0x26f4, 0x01d0, 0x1796, 0xe0e0, 0x1910, 0x160e, 0x70f1,
	0xa1c4, 0x2163, 0xaea7, 0x3a9c, 0x880c, 0xabcb, 0x7ec9, 0xe92a,
	0x9b89, 0xb7e8, 0xbc15, 0x19ae, 0xa80e, 0xa3fa, 0x0f41, 0xf678,
	0x3066, 0x22a6, 0x7394, 0xdcf7, 0xe5fd, 0x2c45, 0xe07a, 0x8310,
	0xef70, 0xf1ef, 0x9f11, 0x0fac, 0x1b78, 0x6e2c, 0x8d3a, 0xb59e,
	0xda37, 0x159b, 0xfdc2, 0xd2dd, 0xbf13, 0x079d, 0x6af0, 0x717e,
	0x26d5, 0x20d0, 0x15b6, 0xd0c2, 0xb031, 0x5d62, 0x5028, 0x72f5,
	0xb5e6, 0xa237, 0xca50, 0xf38b, 0xeb33, 0x92ab, 0xdd71, 0x6bec,
	0x656f, 0x9681, 0xd735, 0x7f46, 0x6e3b, 0x9a3a, 0x0cf9, 0x564b,
	0x2193, 0x5ea7, 0x8d1b, 0x949e, 0xd817, 0x25b9, 0x54e3, 0x99b1,
	0x9fca, 0xd4ac, 0xfe75, 0x7dee, 0xd619, 0x5b57, 0x554e, 0x3ca0,
	0x846a, 0xad07, 0x82af, 0x5861, 0x7b7d, 0x757f, 0x0791, 0x66f0,
	0x11b2, 0xf486, 0xde44, 0x46df, 0x3483, 0xe7e2, 0x2367, 0xba85,
	0xb9c8, 0xecfb, 0x62dc, 0x1df6, 0xd04a, 0x3831, 0x352e, 0x42f3,
	0x38c7, 0xc32e, 0xc512, 0xc974, 0xcfb8, 0x33de, 0x8295, 0x6261,
	0xa0f6, 0x1b72, 0x642c, 0xdd90, 0x8aec, 0x5be9, 0xeb4e, 0xefab,
	0x2aef, 0x7a1c, 0x1c6e, 0x405b, 0x80e5, 0x0243, 0x9ca5, 0xa39f,
	0x6a41, 0xc07e, 0x8d21, 0xae9e, 0x039c, 0x4bb4, 0x375e, 0x22d1,
	0x0494, 0x7bc3, 0xcb7f, 0xd49a, 0xc875, 0xc6a9, 0x6a47, 0xc67e,
	0xbd47, 0x43bf, 0x7cd6, 0xe608, 0xc176, 0x8d30, 0xbf9e, 0x8a9d,
	0x2ae9, 0x7c1c, 0x2c08, 0xad7a, 0xffaf, 0xafff, 0x6a8d, 0x0c7e,
	0xd14b, 0x3120, 0x6cb7, 0x0618, 0xe7e1, 0x2067, 0xa2b6, 0x4b50,
	0xd35e, 0x3402, 0x66e2, 0x03b2, 0x65b4, 0x4d81, 0x3238, 0x6c84,
	0x3518, 0x74f3, 0x8380, 0x7f70, 0x583b, 0x217d, 0xb0a7, 0xcb62,
	0xc99a, 0x21b8, 0x75a7, 0xdf91, 0x9bce, 0xf0e8, 0x9000, 0x6653,
	0xb2b2, 0xce40, 0xc3cf, 0x2412, 0xf7f2, 0xb277, 0x0b40, 0xd73c,
	0x7646, 0x26a2, 0x57d0, 0xb282, 0xfe40, 0x48ee, 0x756d, 0x1591,
	0xf7c2, 0x8277, 0x8061, 0x8643, 0x9425, 0x6317, 0xdee7, 0xe5df,
	0x0e45, 0xfa69, 0x41aa, 0x79f4, 0xec5d, 0xc4dc, 0x0f65, 0xd278,
	0x1a13, 0x0d3d, 0x9a5a, 0x6cf9, 0x4818, 0x836d, 0x9270, 0x0671,
	0x8ee1, 0x76ad, 0xcda2, 0x39fc, 0xf03f, 0x4700, 0xe392, 0x7323,
	0x6bf7, 0x7e6f, 0x4f2a, 0x891a, 0xb5da, 0x9e37, 0x21bd, 0x70a7,
	0xf7c4, 0x8477, 0xb007, 0x6b62, 0xeb6f, 0xceab, 0x28cf, 0x4a3e,
	0xb54f, 0x0b37, 0xa03c, 0xd172, 0x0820, 0xaf0f, 0x9a8d, 0xbbf9,
	0xcdd9, 0x42fc, 0x37c7, 0xbbd1, 0xe5d9, 0x0845, 0xca0f, 0xac8b,
	0x06be, 0x41e1, 0x32f4, 0xa084, 0x6972, 0xeb4d, 0xecab, 0x32dc,
	0x8884, 0x23cb, 0x1685, 0xfbf1, 0xd1bb, 0xc120, 0xdb30, 0x1a8a,
	0x943d, 0x7b17, 0x1f7f, 0x4968, 0xfb7c, 0x5cbb, 0x8139, 0xd652,
	0x1057, 0x1997, 0x910e, 0x6042, 0x93d4, 0xaa60, 0xddd8, 0xc2ec,
	0x0f03, 0xb478, 0x3426, 0x42e2, 0x29c7, 0x4a2f, 0xa44f, 0x8236,
	0xc161, 0x9a30, 0x06f9, 0x06e1, 0x1ee1, 0xdf79, 0x73ce, 0x86f7,
	0x2025, 0xe0b6, 0x4f10, 0xb31a, 0x6e51, 0xf03a, 0x4200, 0xcbc7,
	0x6c9a, 0x2b18, 0x850d, 0xc216, 0xf503, 0x5355, 0x17c6, 0xb0e0,
	0x8c62, 0xe58f, 0x5e45, 0x6f1b, 0xb22b, 0x5740, 0x2282, 0x5794,
	0xf682, 0xca66, 0xc58b, 0x5074, 0x2ef5, 0x4058, 0x83e5, 0x1a70,
	0x6e3d, 0x9c3a, 0x3c9f, 0xbb6a, 0x5ed9, 0xf31b, 0x7b33, 0x3b7f,
	0x631d, 0xd4e7, 0xb575, 0x3137, 0x7bb7, 0xbf7f, 0x6b9d, 0x146f,
	0x01d3, 0x1496, 0xf8d3, 0xeb88, 0x29ab, 0x262f, 0xdad0, 0xf29b,
	0xf322, 0x4233, 0xf8c7, 0xff88, 0x88ff, 0x58cb, 0xd17d, 0x0720,
	0xd7f0, 0xba46, 0x7ac8, 0xc86e, 0xdda9, 0xb3ec, 0x9851, 0x77db,
	0xb3b3, 0xc751, 0x9a56, 0x60f9, 0x28d4, 0x513e, 0x6ce4, 0x5518,
	0x6aa0, 0x217e, 0xb3a7, 0xd351, 0x3b02, 0x1e1d, 0x2379, 0xa485,
	0x4836, 0xad6d, 0xe8af, 0x1698, 0xe6f1, 0x3876, 0x722e, 0x6ee6,
	0x473a, 0xd992, 0xa8a8, 0x05fa, 0x1dd2, 0xf44a, 0x1244, 0x1ab5,
	0xab3d, 0x88c9, 0x6ecb, 0x6a3a, 0xbb7e, 0x4ad9, 0x524f, 0x05d7,
	0x30d2, 0x96a6, 0xf035, 0x4d00, 0xb338, 0x4c51, 0xea29, 0x80ba,
	0x5d43, 0x7128, 0x70d5, 0x85c4, 0x0b16, 0x813c, 0xd352, 0x3802,
	0x062e, 0xd1e1, 0x9b20, 0x1ee8, 0xd679, 0x3b57, 0x4b1d, 0x9e5e,
	0x48bd, 0x266d, 0x98d0, 0xf6db, 0x9366, 0x1860, 0x6e1f, 0xbe3a,
	0x268c, 0x79d0, 0xc85d, 0xeea9, 0x20fe, 0x3bb6, 0xaa1d, 0xa0d8,
	0x3572, 0x1ef3, 0xcd79, 0xe2fc, 0x1532, 0x54c2, 0xb8b1, 0x9dea,
	0xe48e, 0x5754, 0x3682, 0xf6c0, 0x8866, 0xc1cb, 0x3030, 0x74a6,
	0xd680, 0xc257, 0xb403, 0x4f26, 0x851a, -1, -1, -1
};


static unsigned short
chksum (unsigned short init, unsigned n, const unsigned char *s)
{
	unsigned short crc;

	for (crc = init; n > 0; n--)
		crc = crc_table[(crc ^ *s++) & 0xff] ^ (crc >> 8);
	return crc;
}


static int
find_init (int len)
{
	return len < 1024 ? crc_init[len] : -1;
}

/**
 * canon_psa50_gen_crc
 * @pkt: packet data on which to calculate checksum
 * @len: length of @pkt
 *
 * Calculate a CRC on a Canon packet
 *
 * Returns: CRC value. On error doesn't return at all but exits
 *   program with a message to stderr.
 *
 */
int
canon_psa50_gen_crc (const unsigned char *pkt, int len)
{
	int init;

	init = find_init (len);
	if (init != -1)
		return chksum (init, len, pkt);
	fprintf (stderr, _("FATAL ERROR: initial CRC value for length %d unknown\n"), len);
	return -1;
}


static int
guess (const unsigned char *m, int len, int crc)
{
	int i;

	for (i = 0; i < 0x10000; i++)
		if (chksum (i, len, m) == crc)
			return i;
	fprintf (stderr, _("unable to guess initial CRC value\n"));
	return -1;
}

/**
 * canon_psa50_chk_crc:
 * @pkt: packet
 * @len: length of @pkt
 * @crc: presumed checksum
 *
 * Calculate a new checksum for the packet and compare it with an existing checksum to detect transmission errors.
 *
 * Returns: 1 on success or if checksum calculation would fail
 *          0 if checksums don't match
 *
 */
int
canon_psa50_chk_crc (const unsigned char *pkt, int len, unsigned short crc)
{
	unsigned short this;
	int init;

	init = find_init (len);
	if (init != -1)
		return chksum (init, len, pkt) == crc;
	this = guess (pkt, len, crc);
	fprintf (stderr,
		 _("warning: CRC not checked (add len %d, value 0x%04x) #########################\n"),
		 len, this);
	return 1;
}
